/**
 * Copyright 2010 Victor Egea Hernando
 *
 * RockScript is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 * 
 * RockScript is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with RockScript.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "instruction.h"
#include "opcodes.h"

#define OP2STR( OPCODE ) #OPCODE

namespace paper{

typedef struct{
	corelib::uint8 _opcode;
	char *_name;
}opcode_name;

opcode_name opcodes[] = {
    // mathematical opcodes (integer)
	{OP_ADDI,OP2STR(OP_ADDI)},
	{OP_SUBI,OP2STR(OP_SUBI)},
	{OP_MULI,OP2STR(OP_MULI)},
	{OP_DIVI,OP2STR(OP_DIVI)},
	{OP_MODI,OP2STR(OP_MODI)},
	{OP_BWSHLI,OP2STR(OP_BWSHLI)},
	{OP_BWSHRI,OP2STR(OP_BWSHRI)},
	{OP_BWUSHRI,OP2STR(OP_BWUSHRI)},
	{OP_BWANDI,OP2STR(OP_BWANDI)},
	{OP_BWORI,OP2STR(OP_BWORI)},
	{OP_BWXORI,OP2STR(OP_BWXORI)},
	{OP_BWNOTI,OP2STR(OP_BWNOTI)},
	{OP_ABSI,OP2STR(OP_ABSI)},
	{OP_INCI,OP2STR(OP_INCI)},
	{OP_DECI,OP2STR(OP_DECI)},
    // loading/saving opcodes
    {OP_MOVE,OP2STR(OP_MOVE)},
    {OP_SAVE,OP2STR(OP_SAVE)},
	{OP_LOAD,OP2STR(OP_LOAD)},
	{OP_LOADMK,OP2STR(OP_LOADMK)},
	{OP_LOADCK,OP2STR(OP_LOADCK)},
	{OP_LOADIA,OP2STR(OP_LOADIA)},
	{OP_LOADIZ,OP2STR(OP_LOADIZ)},
    // comparison opcodes
    {OP_RCJMP,OP2STR(OP_RCJMP)},
	{OP_RJMP,OP2STR(OP_RJMP)},
	{OP_NOT,OP2STR(OP_NOT)},
	{OP_EQI,OP2STR(OP_EQI)},
	{OP_EQR,OP2STR(OP_EQR)},
	{OP_NEQI,OP2STR(OP_NEQI)},
	{OP_NEQR,OP2STR(OP_NEQR)},
	{OP_LTI,OP2STR(OP_LTI)},
	{OP_LTR,OP2STR(OP_LTR)},
	{OP_LEQI,OP2STR(OP_LEQI)},
	{OP_LEQR,OP2STR(OP_LEQR)},
	{OP_GTI,OP2STR(OP_GTI)},
	{OP_GTR,OP2STR(OP_GTR)},
	{OP_GEI,OP2STR(OP_GEI)},
	{OP_GER,OP2STR(OP_GER)},
    // method opcodes
	{OP_LOADARG,OP2STR(OP_LOADARG)},
	{OP_KCCALL,OP2STR(OP_KCCALL)},
	{OP_UCCALL,OP2STR(OP_UCCALL)},
	{OP_CPPCODE,OP2STR(OP_CPPCODE)},
	{OP_RETURN,OP2STR(OP_RETURN)},
	// object opcodes
	{OP_NEW,OP2STR(OP_NEW)},
    {OP_DELETE,OP2STR(OP_DELETE)}
};

instruction::instruction(){
    // Set operator and operands
    _inst._op = 0; _inst._a = _inst._rest._z = 0;
}

instruction::instruction( corelib::uint8 op, corelib::uint8 a, corelib::int16 z ){
    // Set operator and operands
    _inst._op = op; _inst._a = a; _inst._rest._z = z;
}

instruction::instruction( corelib::uint8 op, corelib::uint8 a, corelib::uint8 b, corelib::uint8 c ){
    // Set operator and operands
    _inst._op = op; _inst._a = a; _inst._rest._bc._b = b; _inst._rest._bc._c = c;
}

instruction::instruction( const __instruction &val ){
    _inst = val;
}

__instruction &instruction::bin( void ){
    return _inst;
}

std::string instruction::str( void ){
    std::stringstream ss;
    // print opcode
	for(corelib::uint32 idx=0;idx<NOPCODES;idx++){
		if(opcodes[idx]._opcode==_inst._op){
			ss << opcodes[idx]._name;
			// print an operand
			ss << " " << static_cast<corelib::int32>(_inst._a);
			// print z or b & c operands
			if( _inst._op & OP_FLAG_TYPEB ){ // op_ & 10000000b
				ss << ", " << static_cast<corelib::int32>(_inst._rest._z);
			}else{
				ss << ", " << static_cast<corelib::int32>(_inst._rest._bc._b);
				ss << ", " << static_cast<corelib::int32>(_inst._rest._bc._c);
			}
    		// return string
    		return ss.str();
		}
	};

    // return string
    return "UNKNOWN";
}

}; // namespace paper;
